; UNSUPPORTED: system-windows
;   See https://github.com/llvm/circt/issues/4128
; RUN: rm -rf %t
; RUN: firtool --repl-seq-mem --repl-seq-mem-file=mems.conf --split-verilog --dedup --emit-omir -o=%t %s
; RUN: FileCheck %s --check-prefix=TESTHARNESS < %t/TestHarness.sv
; RUN: FileCheck %s --check-prefix=DUTMODULE < %t/Prefix_DUTModule.sv
; RUN: FileCheck %s --check-prefix=SEQMEMSGROUP < %t/SeqMemsGroup.sv
; RUN: FileCheck %s --check-prefix=INJECTEDSUBMODULE < %t/Prefix_InjectedSubmodule.sv
; RUN: FileCheck %s --check-prefix=SOMEMODULE < %t/Prefix_SomeModule.sv
; RUN: FileCheck %s --check-prefix=MEM < %t/Prefix_mem.sv
; RUN: FileCheck %s --check-prefix=MEMS-CONF < %t/mems.conf
; RUN: FileCheck %s --check-prefix=SEQMEMS-TXT < %t/SeqMems.txt
; RUN: FileCheck %s --check-prefix=OMIR < %t/omir.json

; Extracted from test/scala/firrtl/ExtractSeqMems.scala
; Checks that instance extraction composes with:
; - Dedup
; - OMIR emission and SRAM path setting
; - Inject DUT Hierarchy
; - Prefixing

circuit TestHarness : %[[
  {
    "class":"sifive.enterprise.firrtl.NestedPrefixModulesAnnotation",
    "target":"~TestHarness|DUTModule",
    "prefix":"Prefix_",
    "inclusive":true
  },
  {
    "class":"sifive.enterprise.firrtl.InjectDUTHierarchyAnnotation",
    "name":"InjectedSubmodule"
  },
  {
    "class":"sifive.enterprise.firrtl.ExtractSeqMemsFileAnnotation",
    "filename":"SeqMems.txt",
    "group":"SeqMemsGroup"
  },
  {
    "class":"sifive.enterprise.firrtl.MarkDUTAnnotation",
    "target":"TestHarness.DUTModule"
  },
  {
    "class": "freechips.rocketchip.objectmodel.OMIRFileAnnotation",
    "filename": "omir.json"
  },
  {
    "class": "freechips.rocketchip.objectmodel.OMIRAnnotation",
    "nodes": [
      {
        "info": "",
        "id": "OMID:0",
        "fields": [
          {"info": "", "name": "omType", "value": ["OMString:OMLazyModule", "OMString:OMSRAM"]},
          {"info": "", "name": "finalPath", "value": "OMMemberInstanceTarget:~TestHarness|SomeModule>mem"}
        ]
      },
      {
        "info": "",
        "id": "OMID:1",
        "fields": [
          {"info": "", "name": "omType", "value": ["OMString:OMLazyModule", "OMString:OMSRAM"]},
          {"info": "", "name": "finalPath", "value": "OMMemberReferenceTarget:~TestHarness|SomeModule_1>mem"}
        ]
      }
    ]
  }
]]
  module SomeModule :
    input clock : Clock
    input reset : Reset
    output io : { flip addr : UInt<3>, flip dataIn : UInt<8>, flip wen : UInt<1>, dataOut : UInt<8>}

    smem mem : UInt<8> [8]
    infer mport read = mem[io.addr], clock
    io.dataOut <= read
    when io.wen :
      infer mport write = mem[io.addr], clock
      write <= io.dataIn

  module SomeModule_1 :
    input clock : Clock
    input reset : Reset
    output io : { flip addr : UInt<3>, flip dataIn : UInt<8>, flip wen : UInt<1>, dataOut : UInt<8>}

    smem mem : UInt<8> [8]
    infer mport read = mem[io.addr], clock
    io.dataOut <= read
    when io.wen :
      infer mport write = mem[io.addr], clock
      write <= io.dataIn

  ; DUTMODULE: module Prefix_DUTModule
  ; DUTMODULE: SeqMemsGroup SeqMemsGroup
  ; DUTMODULE: Prefix_InjectedSubmodule InjectedSubmodule
  ; NOTE(fschuiki): The above should actually read `Prefix_SeqMemsGroup`, but it
  ; is not yet totally clear how to properly order the FIRRTL passes such that
  ; the module generated by `ExtractInstances` gets properly prefixed.
  module DUTModule :
    input clock : Clock
    input reset : Reset
    output io : { foo : { flip addr : UInt<3>, flip dataIn : UInt<8>, flip wen : UInt<1>, dataOut : UInt<8>}, bar : { flip addr : UInt<3>, flip dataIn : UInt<8>, flip wen : UInt<1>, dataOut : UInt<8>}}

    inst inst0 of SomeModule
    inst0.clock <= clock
    inst0.reset <= reset
    io.foo.dataOut <= inst0.io.dataOut
    inst0.io.wen <= io.foo.wen
    inst0.io.dataIn <= io.foo.dataIn
    inst0.io.addr <= io.foo.addr
    inst inst1 of SomeModule_1
    inst1.clock <= clock
    inst1.reset <= reset
    io.bar.dataOut <= inst1.io.dataOut
    inst1.io.wen <= io.bar.wen
    inst1.io.dataIn <= io.bar.dataIn
    inst1.io.addr <= io.bar.addr

  ; TESTHARNESS: module TestHarness
  ; TESTHARNESS: Prefix_DUTModule dut
  ; TESTHARNESS-NOT: mem_ext mem_ext
  ; TESTHARNESS-NOT: Prefix_mem_ext mem_ext
  module TestHarness :
    input clock : Clock
    input reset : UInt<1>
    output io : { foo : { flip addr : UInt<3>, flip dataIn : UInt<8>, flip wen : UInt<1>, dataOut : UInt<8>}, bar : { flip addr : UInt<3>, flip dataIn : UInt<8>, flip wen : UInt<1>, dataOut : UInt<8>}}

    inst dut of DUTModule
    dut.clock <= clock
    dut.reset <= reset
    io.bar.dataOut <= dut.io.bar.dataOut
    dut.io.bar.wen <= io.bar.wen
    dut.io.bar.dataIn <= io.bar.dataIn
    dut.io.bar.addr <= io.bar.addr
    io.foo.dataOut <= dut.io.foo.dataOut
    dut.io.foo.wen <= io.foo.wen
    dut.io.foo.dataIn <= io.foo.dataIn
    dut.io.foo.addr <= io.foo.addr

  ; INJECTEDSUBMODULE: module Prefix_InjectedSubmodule
  ; INJECTEDSUBMODULE: Prefix_SomeModule inst0
  ; INJECTEDSUBMODULE: Prefix_SomeModule inst1

  ; SOMEMODULE: module Prefix_SomeModule
  ; SOMEMODULE: Prefix_mem mem

  ; SEQMEMSGROUP: module SeqMemsGroup
  ; SEQMEMSGROUP:   mem_wiring_1_R0_addr
  ; SEQMEMSGROUP:   mem_wiring_0_R0_addr
  ; SEQMEMSGROUP: Prefix_mem_ext mem_ext
  ; SEQMEMSGROUP: Prefix_mem_ext mem_ext

  ; MEM: module Prefix_mem
  ; MEM-NOT: mem_ext mem_ext
  ; MEM-NOT: Prefix_mem_ext mem_ext

  ; MEMS-CONF: name Prefix_mem_ext depth 8 width 8 ports write,read
  ; SEQMEMS-TXT: mem_wiring_1 -> Prefix_DUTModule.InjectedSubmodule.inst0.mem
  ; SEQMEMS-TXT: mem_wiring_0 -> Prefix_DUTModule.InjectedSubmodule.inst1.mem

  ; OMIR:      "id": "OMID:0"
  ; OMIR:      "name": "finalPath"
  ; OMIR-NEXT: "value": "OMMemberInstanceTarget:~Prefix_DUTModule|Prefix_DUTModule/SeqMemsGroup:SeqMemsGroup/mem_ext{{[^:]*}}:Prefix_mem_ext"
  ; OMIR:      "id": "OMID:1"
  ; OMIR:      "name": "finalPath"
  ; OMIR-NEXT: "value": "OMMemberInstanceTarget:~Prefix_DUTModule|Prefix_DUTModule/SeqMemsGroup:SeqMemsGroup/mem_ext{{[^:]*}}:Prefix_mem_ext"
